package order

import (
	"errors"
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

type tMockOrderRepository struct {
	rwmutex *sync.RWMutex
	orders  map[int64]*tOrderHeaderEntity
	items   map[int64]*tOrderItemEntity
}

func newMockOrderRepository() iOrderRepository {
	it := new(tMockOrderRepository)
	it.init()
	return it
}

func (me *tMockOrderRepository) init() {
	me.rwmutex = new(sync.RWMutex)
	me.orders = make(map[int64]*tOrderHeaderEntity)
	me.items = make(map[int64]*tOrderItemEntity)
}

func (me *tMockOrderRepository) LoadOrderHeader(orderID int64) (error, *tOrderHeaderEntity) {
	me.rwmutex.RLock()
	defer me.rwmutex.RUnlock()

	it, ok := me.orders[orderID]
	if ok {
		return nil, it.Clone()
	}

	return gErrorNotFound, nil
}

func (me *tMockOrderRepository) SaveOrderHeader(it *tOrderHeaderEntity) (error, *tOrderHeaderEntity) {
	me.rwmutex.Lock()
	defer me.rwmutex.Unlock()

	origin, ok := me.orders[it.OrderID]
	if ok {
		if origin.Status != it.Status || origin.Timestamp != it.Timestamp {
			return gErrorVersionChanged, nil
		}
	}

	it.Timestamp = time.Now().UnixNano()
	me.orders[it.OrderID] = it.Clone()
	return nil, it
}

func (me *tMockOrderRepository) LoadOrderItem(itemID int64) (error, *tOrderItemEntity) {
	me.rwmutex.RLock()
	defer me.rwmutex.RUnlock()

	it, ok := me.items[itemID]
	if ok {
		return nil, it.Clone()
	}

	return gErrorNotFound, nil
}

func (me *tMockOrderRepository) SaveOrderItem(it *tOrderItemEntity) (error, *tOrderItemEntity) {
	me.rwmutex.Lock()
	defer me.rwmutex.Unlock()

	origin, ok := me.items[it.ItemID]
	if ok {
		if origin.Timestamp != it.Timestamp {
			return gErrorVersionChanged, nil
		}
	}

	it.Timestamp = time.Now().UnixNano()
	me.items[it.ItemID] = it.Clone()
	return nil, it
}

func (me *tMockOrderRepository) RemoveOrderItem(it *tOrderItemEntity) error {
	me.rwmutex.Lock()
	defer me.rwmutex.Unlock()

	origin, ok := me.items[it.ItemID]
	if ok {
		if origin.Timestamp != it.Timestamp {
			return gErrorVersionChanged
		}
	}

	delete(me.items, it.ItemID)
	return nil
}

func (me *tMockOrderRepository) LoadOrderItemsByOrderID(orderID int64) (error, []*tOrderItemEntity) {
	me.rwmutex.Lock()
	defer me.rwmutex.Unlock()

	lst := []*tOrderItemEntity{}
	for _, v := range me.items {
		if v.OrderID == orderID {
			lst = append(lst, v)
		}
	}

	return nil, lst
}

func (me *tMockOrderRepository) NewOrderID() int64 {
	return atomic.AddInt64(&gOrderID, 1)
}

func (me *tMockOrderRepository) NewItemID() int64 {
	return atomic.AddInt64(&gItemID, 1)
}

func (me *tMockOrderRepository) Transaction(action func() error) error {
	fmt.Println("tMockOrderRepository.Transaction begin")
	e := action()

	if e != nil {
		fmt.Printf("tMockOrderRepository.Transaction rollback, e=%v\n", e)
	} else {
		fmt.Println("tMockOrderRepository.Transaction commit")
	}
	return e
}

var gErrorNotFound = errors.New("not found")
var gErrorVersionChanged = errors.New("version changed")
var MockOrderRepository = newMockOrderRepository()
var gOrderID = time.Now().UnixNano()
var gItemID = time.Now().UnixNano()
